/*
 * fetch.c - cve-check-tool helpers
 *
 * Copyright (C) 2015 Intel Corporation
 *
 * cve-check-tool is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#define _GNU_SOURCE

#include <curl/curl.h>
#include <gio/gio.h>
#include <glib.h>
#include <limits.h>
#include <stdlib.h>
#include <sys/stat.h>

#include "fetch.h"
#include "util.h"

struct fetch_t {
        FILE *f;
        const char *target;
};

static size_t write_func(void *ptr, size_t size, size_t nmemb, struct fetch_t *f)
{
        if (!f->f) {
                f->f = fopen(f->target, "wb");
        }
        if (!f->f) {
                return -1;
        }
        return fwrite(ptr, size, nmemb, f->f);
}

FetchStatus fetch_uri(const char *uri, const char *target, bool verbose, const char *cacert_file)
{
        FetchStatus ret = FETCH_STATUS_FAIL;
        CURLcode res;
        struct stat st;
        CURL *curl = NULL;
        struct fetch_t *f = NULL;

        printf("fetch_uri: uri=%s target=%s cacert_file=%s\n",uri,target,cacert_file);
        curl = curl_easy_init();
        if (!curl) {
                return ret;
        }

        if (cacert_file) {
                res = curl_easy_setopt(curl, CURLOPT_CAINFO, cacert_file);
                if (res != CURLE_OK) {
                        goto bail;
                }
        }

        if (stat(target, &st) == 0) {
                res = curl_easy_setopt(curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
                if (res != CURLE_OK) {
                        goto bail;
                }
                res = curl_easy_setopt(curl, CURLOPT_TIMEVALUE, st.st_mtime);
                if (res != CURLE_OK) {
                        goto bail;
                }
        }

        res = curl_easy_setopt(curl, CURLOPT_URL, uri);
        if (res != CURLE_OK) {
                goto bail;
        }
        if (verbose) {
                (void)curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
        }
        res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, (curl_write_callback)write_func);
        if (res != CURLE_OK) {
                goto bail;
        }
        f = calloc(1, sizeof(struct fetch_t));
        if (!f) {
                goto bail;
        }
        f->target = target;
        res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
        if (res != CURLE_OK) {
                goto bail;
        }

        /* Handle future redirects from NVD */
        res = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
        if (res != CURLE_OK) {
                goto bail;
        }
        res = curl_easy_perform(curl);
        if (res != CURLE_OK) {
                goto bail;
        }
        ret = f->f ? FETCH_STATUS_UPDATE : FETCH_STATUS_OK;

bail:

        if (f) {
                if (f->f) {
                        fclose(f->f);
                }
                free(f);
        }

        if (curl) {
                curl_easy_cleanup(curl);
        }

        return ret;
}

bool gunzip_file(const char *path)
{
        GFile *in = NULL, *out = NULL;
        autofree(gchar) *newpath = NULL;
        autofree(GFileInputStream) *fis = NULL;
        autofree(GFileOutputStream) *fos = NULL;
        autofree(GOutputStream) *cos = NULL;
        autofree(GZlibDecompressor) *conv = NULL;
        gsize ret;

        newpath = g_strdup(path);

        if (g_str_has_suffix(newpath, ".gz")) {
                newpath = str_replace(newpath, ".gz", "");
        }

        in = g_file_new_for_path(path);
        out = g_file_new_for_path(newpath);

        fis = g_file_read(in, NULL, NULL);
        if (!fis) {
                return NULL;
        }
        fos = g_file_replace(out, NULL, FALSE, 0, NULL, NULL);
        if (!fos) {
                return NULL;
        }

        conv = g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_GZIP);
        cos = g_converter_output_stream_new(G_OUTPUT_STREAM(fos), G_CONVERTER(conv));
        if (!cos) {
                return NULL;
        }
        ret = g_output_stream_splice(cos, G_INPUT_STREAM(fis), G_OUTPUT_STREAM_SPLICE_NONE, NULL, NULL);
        return (ret > 0 ? true : false);
}

/*
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
 *
 * Local variables:
 * c-basic-offset: 8
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * vi: set shiftwidth=8 tabstop=8 expandtab:
 * :indentSize=8:tabSize=8:noTabs=true:
 */
